/*
* fdc.c - Mokri
* Contributors: xMine
* (Any new Contributor may add himself/herself to the list)
* Fixes to be made: 
* Source: forum.osdev.org
*/

#include "fdc.h"

#if CACHE_ON_LOAD_SECTORS > 2880
#error CACHE_ON_LOAD_SECTORS > 2880 ist nicht erlaubt!
#endif

// standard base address of the primary floppy controller
static const int floppy_base = 0x03F0;
// standard IRQ number for floppy controllers
//static const int floppy_irq = 6;

volatile uint32_t irq_count;
enum { floppy_motor_off = 0, floppy_motor_on, floppy_motor_wait };
volatile int floppy_motor_ticks = 0;
volatile int floppy_motor_state = 0;

// we statically reserve a totally uncomprehensive amount of memory
// must be large enough for whatever DMA transfer we might desire
// and must not cross 64k borders so easiest thing is to align it
// to 2^N boundary at least as big as the block
#define floppy_dmalen 0x4800
char floppy_dmabuf[floppy_dmalen] __attribute__((aligned(0x8000)));

// Diese Variable zeigt, welcher Zylinder als letztes gelesen wurde
uint32_t last_cyl;
lock_t fdc_lock = UNLOCKED;
lock_t fdc_write_lock = UNLOCKED;

#ifdef LOAD_FLOPPY_IN_BACKGROUND
volatile BOOL fdd_read_req = FALSE;
#endif

volatile char *floppy_cache[2*80*18]; // Köpfe * Spuren * Sektoren pro Spur
#if WRITE_CACHE_SIZE > 0
static volatile uint32_t queue[WRITE_CACHE_SIZE];
static volatile uint32_t queue_len = 0;
#endif

static const char * drive_types[8] = {
    "none",
    "360kB 5.25\"",
    "1.2MB 5.25\"",
    "720kB 3.5\"",

    "1.44MB 3.5\"",
    "2.88MB 3.5\"",
    "unknown type",
    "unknown type"
};

void floppy_handler () {
  irq_count++;
}

/* Setzt den IRQ Counter zurück
 */
void floppy_reset_count () {
  irq_count = 0;
}

/* Wartet darauf, dass der IRQ6 x mal aufgerufen wurde
 * Parameter: Zu wartende Aufrufe
 * Rückgaben: Keine
 */
void floppy_wait_irq (int cycles) {
  while (irq_count < cycles) {
    asm volatile ("nop");
  }
}

// Obviously you'd have this return the data, start drivers or something.
void floppy_detect_drives () {

   outb(0x70, 0x10);
   unsigned drives = inb(0x71);

   printf("\tFloppy drive 0: %s\n", drive_types[drives >> 4]);
   printf("\tFloppy drive 1: %s\n", drive_types[drives & 0xf]);
}

void floppy_write_cmd(int base, char cmd) {
    int i; // do timeout, 60 seconds
    for(i = 0; i < 600; i++) {
        sleep (10); // sleep 10 ms
        if( (inb(base+FLOPPY_MSR) & 0x80) == 0x80) {
		outb(base+FLOPPY_FIFO, cmd);
		return;
        }
    }
    
    kernel_panic("floppy_write_cmd: timeout");
}

unsigned char floppy_read_data(int base) {

    int i; // do timeout, 60 seconds
    for(i = 0; i < 600; i++) {
        sleep(10); // sleep 10 ms
        if(0x80 & inb(base+FLOPPY_MSR)) {
            return inb(base+FLOPPY_FIFO);
        }
    }
    kernel_panic("floppy_read_data: timeout");
    return 0; // not reached
}

void floppy_check_interrupt(int base, int *st0, int *cyl) {
    
    floppy_write_cmd(base, CMD_SENSE_INTERRUPT);

    *st0 = floppy_read_data(base);
    *cyl = floppy_read_data(base);
}

// Move to cylinder 0, which calibrates the drive..
int floppy_calibrate(int base) {

    int i, st0, cyl = -1; // set to bogus cylinder

    floppy_motor(base, floppy_motor_on);

    for(i = 0; i < 10; i++) {
        // Attempt to positions head to cylinder 0
	floppy_reset_count ();
	floppy_write_cmd(base, CMD_RECALIBRATE);
        floppy_write_cmd(base, 0); // argument is drive, we only support 0

	floppy_wait_irq (1);
        floppy_check_interrupt(base, &st0, &cyl);
        
        if(st0 & 0xC0) {
            static const char * status[] =
            { 0, "error", "invalid", "drive" };
            printf("floppy_calibrate: status = %s\n", status[st0 >> 6]);
            continue;
        }

        if(!cyl) { // found cylinder 0 ?
            floppy_motor(base, floppy_motor_off);
            return 0;
        }
    }

    printf("floppy_calibrate: 10 retries exhausted\n");
    floppy_motor(base, floppy_motor_off);
    return -1;
}

void floppy_select_drive(uint8_t drive) {
      uint8_t tmp = inb(0x3F2);
      outb(0x3F2, (tmp & (~0x03)) | drive);
 }

int floppy_reset(int base) {

    outb(base + FLOPPY_DOR, 0x00); // disable controller
    outb(base + FLOPPY_DOR, 0x0C); // enable controller

    //irq_wait(floppy_irq); // sleep until interrupt occurs

    {
        int st0, cyl; // ignore these here..
        floppy_check_interrupt(base, &st0, &cyl);
    }

    // set transfer speed 500kb/s
    outb(base + FLOPPY_CCR, 0x00);

    //  - 1st byte is: bits[7:4] = steprate, bits[3:0] = head unload time
    //  - 2nd byte is: bits[7:1] = head load time, bit[0] = no-DMA
    // 
    //  steprate    = (8.0ms - entry*0.5ms)*(1MB/s / xfer_rate)
    //  head_unload = 8ms * entry * (1MB/s / xfer_rate), where entry 0 -> 16
    //  head_load   = 1ms * entry * (1MB/s / xfer_rate), where entry 0 -> 128
    //
    floppy_write_cmd(base, CMD_SPECIFY);
    floppy_write_cmd(base, 0xdf); /* steprate = 3ms, unload time = 240ms */
    floppy_write_cmd(base, 0x02); /* load time = 16ms, no-DMA = 0 */

    // it could fail...
    if(floppy_calibrate(base)) return -1;
    return TRUE;
}

void floppy_motor(int base, int onoff) {

    if(onoff) {
        if(!floppy_motor_state) {
            // need to turn on
            outb(base + FLOPPY_DOR, 0x1c);
            sleep(500); // wait 500 ms = hopefully enough for modern drives
        }
        floppy_motor_state = floppy_motor_on;
    } else {
        if(floppy_motor_state == floppy_motor_wait) {
            printf("floppy_motor: strange, fd motor-state already waiting..\n");
        }
        floppy_motor_ticks = 600; // 3 seconds, see floppy_timer() below
        floppy_motor_state = floppy_motor_wait;
    }
}

void floppy_motor_kill(int base) {
    outb(base + FLOPPY_DOR, 0x0c);
    floppy_motor_state = floppy_motor_off;
}

//
// THIS SHOULD BE STARTED IN A SEPARATE THREAD.
//
//
void floppy_timer() {
    while(1) {
        // sleep for 500ms at a time, which gives around half
        // a second jitter, but that's hardly relevant here.
        sleep(500);
        if(floppy_motor_state == floppy_motor_wait) {
            floppy_motor_ticks -= 50;
            if(floppy_motor_ticks <= 0) {
                floppy_motor_kill(floppy_base);
            }
        }
    }
}

// Seek for a given cylinder, with a given head
int floppy_seek(int base, unsigned int cyli, int head) {

    unsigned int i;
    int st0, cyl = -1; // set to bogus cylinder

    floppy_motor(base, floppy_motor_on);

    for(i = 0; i < 10; i++) {
        // Attempt to position to given cylinder
        // 1st byte bit[1:0] = drive, bit[2] = head
        // 2nd byte is cylinder number
        floppy_reset_count ();
        floppy_write_cmd(base, CMD_SEEK);
        floppy_write_cmd(base, head<<2);
        floppy_write_cmd(base, cyli);

        floppy_wait_irq (1); 
        floppy_check_interrupt(base, &st0, &cyl);

        if(st0 & 0xC0) {
            static const char * status[] =
            { "normal", "error", "invalid", "drive" };
            printf ("floppy_seek: status = %s\n", status[st0 >> 6]);
            continue;
        }

        if(cyl == cyli) {
            floppy_motor(base, floppy_motor_off);
            return 0;
        }

    }

    printf ("floppy_seek: 10 retries exhausted\n");
    floppy_motor(base, floppy_motor_off);
    return -1;
}

void floppy_dma_init(floppy_dir dir) {

    union {
        unsigned char b[4]; // 4 bytes
        unsigned long l;    // 1 long = 32-bit
    } a, c; // address and count

    a.l = (unsigned) &floppy_dmabuf;
    c.l = (unsigned) floppy_dmalen - 1; // -1 because of DMA counting

    // check that address is at most 24-bits (under 16MB)
    // check that count is at most 16-bits (DMA limit)
    // check that if we add count and address we don't get a carry
    // (DMA can't deal with such a carry, this is the 64k boundary limit)
    if((a.l >> 24) || (c.l >> 16) || (((a.l&0xffff)+c.l)>>16)) {
        kernel_panic("floppy_dma_init: static buffer problem\n");
    }

    unsigned char mode;
    switch(dir) {
        // 01:0:0:01:10 = single/inc/no-auto/to-mem/chan2
        case floppy_dir_read:  mode = 0x46; break;
        // 01:0:0:10:10 = single/inc/no-auto/from-mem/chan2
        case floppy_dir_write: mode = 0x4a; break;
        default: kernel_panic("floppy_dma_init: invalid direction");
                 return; // not reached, please "mode user uninitialized"
    }

    outb(0x0a, 0x06);   // mask chan 2

    outb(0x0c, 0xff);   // reset flip-flop
    outb(0x04, a.b[0]); //  - address low byte
    outb(0x04, a.b[1]); //  - address high byte

    outb(0x81, a.b[2]); // external page register

    outb(0x0c, 0xff);   // reset flip-flop
    outb(0x05, c.b[0]); //  - count low byte
    outb(0x05, c.b[1]); //  - count high byte

    outb(0x0b, mode);   // set mode (see above)

    outb(0x0a, 0x02);   // unmask chan 2
}

// This monster does full cylinder (both tracks) transfer to
// the specified direction (since the difference is small).
//
// It retries (a lot of times) on all errors except write-protection
// which is normally caused by mechanical switch on the disk.
//
int floppy_do_track(int base, unsigned int cyl, floppy_dir dir) {
    
    // transfer command, set below
    unsigned char cmd;

    // Read is MT:MF:SK:0:0:1:1:0, write MT:MF:0:0:1:0:1
    // where MT = multitrack, MF = MFM mode, SK = skip deleted
    // 
    // Specify multitrack and MFM mode
    static const int flags = 0xC0;
    switch(dir) {
        case floppy_dir_read:
            cmd = CMD_READ_DATA | flags;
            break;
        case floppy_dir_write:
            cmd = CMD_WRITE_DATA | flags;
            break;
        default: 

            kernel_panic("floppy_do_track: invalid direction");
            return 0; // not reached, but pleases "cmd used uninitialized"
    }

    // seek both heads
    if(floppy_seek(base, cyl, 0)) {
      return -1;
    }
    if(floppy_seek(base, cyl, 1)) {
	    return -1;
    }

    int i;
    for(i = 0; i < 20; i++) {
        floppy_motor(base, floppy_motor_on);

        // init dma..
        floppy_dma_init(dir);
	floppy_reset_count ();

        sleep(100); // give some time (100ms) to settle after the seeks

        floppy_write_cmd(base, cmd);  // set above for current direction
	floppy_write_cmd(base, 0);    // 0:0:0:0:0:HD:US1:US0 = head and drive
	floppy_write_cmd(base, cyl);  // cylinder
	floppy_write_cmd(base, 0);    // first head (should match with above)
	floppy_write_cmd(base, 1);    // first sector, strangely counts from 1
	floppy_write_cmd(base, 2);    // bytes/sector, 128*2^x (x=2 -> 512)
	floppy_write_cmd(base, 18);   // number of tracks to operate on
	floppy_write_cmd(base, 27);   // GAP3 length, 27 is default for 3.5"
	floppy_write_cmd(base, 0xff); // data length (0xff if B/S != 0)
	
	floppy_wait_irq (1);

        // first read status information
        unsigned char st0, st1, st2, rcy, rhe, rse, bps;
        st0 = floppy_read_data(base);
        st1 = floppy_read_data(base);
        st2 = floppy_read_data(base);
        rcy = floppy_read_data(base);
        rhe = floppy_read_data(base);
        rse = floppy_read_data(base);
        // bytes per sector, should be what we programmed in
        bps = floppy_read_data(base);

        int error = 0;

        if(st0 & 0xC0) {
            static const char * status[] =
            { 0, "error", "invalid command", "drive not ready" };
            printf("floppy_do_sector: status = %s\n", status[st0 >> 6]);
            error = 1;
        }
        if(st1 & 0x80) {
            printf("floppy_do_sector: end of cylinder\n");
            error = 1;
        }
        if(st0 & 0x08) {
            printf("floppy_do_sector: drive not ready\n");
            error = 1;
        }
        if(st1 & 0x20) {
            printf("floppy_do_sector: CRC error\n");
            error = 1;
        }
        if(st1 & 0x10) {
            printf("floppy_do_sector: controller timeout\n");
            error = 1;
        }
        if(st1 & 0x04) {
            printf("floppy_do_sector: no data found\n");
            error = 1;
        }
        if((st1|st2) & 0x01) {
            printf("floppy_do_sector: no address mark found\n");
            error = 1;
        }
        if(st2 & 0x40) {
            printf("floppy_do_sector: deleted address mark\n");
            error = 1;
        }
        if(st2 & 0x20) {
            printf("floppy_do_sector: CRC error in data\n");
            error = 1;
        }
        if(st2 & 0x10) {
            printf("floppy_do_sector: wrong cylinder\n");
            error = 1;
        }
        if(st2 & 0x04) {
            printf("floppy_do_sector: uPD765 sector not found\n");
            error = 1;
        }
        if(st2 & 0x02) {
            printf("floppy_do_sector: bad cylinder\n");
            error = 1;
        }
        if(bps != 0x2) {
            printf("floppy_do_sector: wanted 512B/sector, got %i", (1<<(bps+7)));
            error = 1;
        }
        if(st1 & 0x02) {
            printf("floppy_do_sector: not writable\n");
            error = 2;
        }
        

        if(!error) {
            floppy_motor(base, floppy_motor_off);
            return 0;
        }
        if(error > 1) {
            printf("floppy_do_sector: not retrying..\n");
            floppy_motor(base, floppy_motor_off);
            print ("Kritischer Fehler aufgetreten\n");
	    return -2;
        }
        
    }

    printf("floppy_do_sector: 20 retries exhausted\n");
    floppy_motor(base, floppy_motor_off);
    return -1;

}

int floppy_read_track(int base, unsigned int cyl) {
    return floppy_do_track(base, cyl, floppy_dir_read);
}

int floppy_write_track(int base, unsigned int cyl) {
    return floppy_do_track(base, cyl, floppy_dir_write);
}

/* Liest einen Sektor und kopiert diesen ein einen bereit gestellten Puffer
 * Parameter: Sektornummer in LBA Notation, Puffer[512]
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL floppy_read_sector (uint32_t lba, void *buffer) {
	uint32_t C = lba / 36; // 2 Köpfe * 18 Sektoren pro Spur
	uint32_t S = lba % 36;
	
	if (C == last_cyl) { // Wurde diese Spur als letztes gelesen? Dann liegt diese noch im Cache
		memcpy (buffer, floppy_dmabuf+(S*512), 512); // Gewünschten Sektor kopieren
		//print ("C");
		//print ("vom Cache\n");
		return TRUE;
	}
	
	if (floppy_read_track (floppy_base, C) == 0) { // Track lesen
		memcpy (buffer, floppy_dmabuf+(S*512), 512); // Gewünschten Sektor kopieren
		//print ("von fdd\n");
		last_cyl = C;
		return TRUE;
	}
	
	return FALSE;
}

/* Schreibt einen Sektor auf eine Diskette
 * Parameter: Sektornummer in LBA Notation, Puffer[512]
 * Rückgaben: Bei Erfolg TRUE, ansonsten FALSE
 */
BOOL floppy_write_sector (uint32_t lba, void *buffer) {
	#ifndef FLOPPY_READ_ONLY
	uint32_t C = lba / 36;
	uint32_t S = lba % 36;
	
	if (C != last_cyl) { // Wenn dieser Zylinder noch nicht im Cache ist, dann diesen auslesen
		if (floppy_read_track (floppy_base, C) != 0) {
			return FALSE;
		}
		
		last_cyl = C;
	}
	
	memcpy (floppy_dmabuf+(S*512), buffer, 512); // Buffer in den Cache kopieren
	
	if (floppy_write_track (floppy_base, C) == 0) { // Und raus damit
		return TRUE;
	}
	
	return FALSE;
	#else
	return TRUE; // Wenn der define gesetzt ist geben wir immer TRUE zurück, schreiben aber nichts
	#endif
}

#if WRITE_CACHE_SIZE > 0
void floppy_write_queue () {
	uint32_t sektor;
	
	for (;;) {
		if (queue_len < 1) { // Wenn nichts zu tun ist, rufen wir einfach den scheduler wieder auf
			asm volatile ("int $0x20");
			continue;
		}
		
		sektor = queue[0];
		memcpy ((void*)queue, (void*)(queue+sizeof(uint32_t)), queue_len-1); // Liste nachrücken lassen
		queue_len--; // Jetzt ist wieder Platz
		
		lock_wait (&fdc_lock);
		fdc_lock = LOCKED;
		floppy_write_sector (sektor, (void*)floppy_cache[sektor]); // Nun noch schreiben...
		fdc_lock = UNLOCKED;
	}
}
#endif

#ifdef LOAD_FLOPPY_IN_BACKGROUND
void floppy_load_in_background () {
	int i;
	char buffer[512];
	for (i=0; i<2880; i++) {
		if (floppy_cache[i] != NULL) {
			continue;
		}
		
		lock_wait (&fdc_lock);
		fdc_lock = LOCKED;
		
		floppy_read_sector (i, buffer);
		
		// Es *kann* passieren, dass der Buffer inzwischen exestiert (Und vll. geändert wurde)
		if (floppy_cache[i] == NULL) {
			floppy_cache[i] = (char*)malloc (512);
			memcpy ((void*)floppy_cache[i], buffer, 512);
		}
		
		fdc_lock = UNLOCKED;
		
		if (fdd_read_req) {
			//printf ("fdd_read_req = TRUE\n");
			asm volatile ("int $0x20"); // Anderen Prozessen die Chanche geben etwas zu machen
			sleep (1000);
		}
		
	}
	
	//printf ("FDD liegt im Ram\n");
	
	DestroyTask (GetRunningTaskPID(), 0); // Diesen Thread brauchen wir nicht mehr
}
#endif

BOOL floppy_init () {
	memset (floppy_cache, 0x0, 2880*4);
	memset (floppy_dmabuf, 0x0, floppy_dmalen);
	last_cyl = 0xFFFFFFFF;

	if (floppy_reset (floppy_base) != TRUE) {
		return FALSE;
	}

	floppy_select_drive (0x00);
	
	/* Treiber bekannt machen */
	fs_announce_driver (fdc_init, fdc_open, fdc_close, fdc_read, fdc_write, fdc_seek, fdc_tell,
			    NULL, NULL, NULL, NULL, NULL, NULL, NULL, fdc_deinit);

	#ifdef CACHE_FLOPPY_ON_LOAD
	int i; // Hier lesen wir die Floppy aus und kopieren sie in den Cache
	for (i=0; i<CACHE_ON_LOAD_SECTORS; i++) {
		floppy_cache[i] = (char*)malloc (512);
		floppy_read_sector (i, (void*)floppy_cache[i]);
	}
	#endif
	
	// Wenn die Schreib Queue aktiviert ist, starten wir hier ihren Thread
	#if WRITE_CACHE_SIZE > 0
	memset ((void*)queue, 0x0, 4*WRITE_CACHE_SIZE);
	mt_createThread (&floppy_write_queue, TRUE);
	#endif
	
	#ifdef LOAD_FLOPPY_IN_BACKGROUND
	mt_createThread (&floppy_load_in_background, TRUE);
	#endif
	
	return TRUE;
}

/** VFS Treiber **/

struct fdc_struct {
	char path[256];
	int filelen;
	int handle;
	struct fdc_struct *next;
};

struct fdc_struct *fdc_first_handle = NULL;
struct fdc_struct *fdc_handle_handle = NULL;
int fdc_cur = 0;
char fdc_nr;

BOOL fdc_init (char nr) {
	
	struct fs_entry *dev = fs_find_entry ("/dev");
	fdc_nr = nr;
	
	if (dev == NULL) {
		print ("fdc_init: Konnte /dev nicht finden\n");
	}
	
	if (fs_add_node ("fd", 0x0, dev, nr) == NULL) { // /dev/fd <- Floppy Blockgerät
		print ("fdc_init: Konnte nicht /dev/fd erstellen\n");
	}
	
	return TRUE;
}

int fdc_add_handle (struct fdc_struct *handle) {
	handle->handle = fdc_cur;
	fdc_cur++;
	
	if (fdc_first_handle == NULL) {
		fdc_first_handle = handle;
		fdc_handle_handle = handle;
		handle->next = NULL;
	} else {
		fdc_handle_handle->next = handle;
		fdc_handle_handle = handle;
		handle->next = NULL;
	}
	
	return fdc_cur - 1;
}

void fdc_del_handle (int handle_num) {
	struct fdc_struct *ptr = fdc_first_handle;
	
	if (ptr == NULL) {
		return;
	}
	
	if (ptr->next == NULL) { // Einziger offener Handle?
		free (ptr);
		fdc_first_handle = fdc_handle_handle = NULL;
	}
	
	while (ptr != NULL) { // Handle suchen
		if (ptr->next->handle == handle_num) {
			struct fdc_struct *tmp = ptr->next;
			ptr->next = tmp->next;
			if (tmp == fdc_handle_handle) {
				fdc_handle_handle = ptr;
			}
			free (tmp);
			break;
		}
		
		ptr = ptr->next;
	}
}

struct fdc_struct *fdc_get_handle (int handle_num) {
	struct fdc_struct *ptr = fdc_first_handle;
	
	while (ptr != NULL) {
		if (ptr->handle == handle_num) {
			return ptr;
		}
		
		ptr = ptr->next;
	}
	
	return NULL;
}

BOOL fdc_deinit () {
	return TRUE;
}

FILE *fdc_open (const char *path, struct fs_entry *entry, const char *params) {
	
	if (_strcmp (path, "/dev/fd")) {
		struct fdc_struct *h = (struct fdc_struct *) malloc (sizeof (struct fdc_struct));
		FILE *f = (FILE*) malloc (sizeof (FILE));
		h->filelen = 2*80*18*512; // Größe einer normalen Diskette
		strcpy (h->path, path);
		int num = fdc_add_handle (h);
		f->driver = fdc_nr;
		f->fpos = 0;
		f->handle = num;
		f->access = fs_parse_params (params);
		
		return f;
	}
	
	return NULL;
}

size_t fdc_read (FILE *handle, char *buffer, size_t bytes) {
	struct fdc_struct *h = fdc_get_handle (handle->handle);
	
	if (h == NULL) {
		return FS_ERR_EOF;
	}
	
	if (_strcmp (h->path, "/dev/fd")) {
		if (bytes > 512) { // Maximal ein Sektor pro fread
			bytes = 512;
		}
		
		char buf[512];
		size_t offset = handle->fpos % 512; // Verschiebung im Sektor
		uint32_t sektor = handle->fpos/512;
		
		if (floppy_cache[sektor] == NULL) { // Liegt dieser Sektor schon im Cache?
			#ifdef LOAD_FLOPPY_IN_BACKGROUND
			while (fdd_read_req);
			fdd_read_req = TRUE;
			#endif
			lock_wait (&fdc_lock); // Damit verhindern wir, dass mehrere Prozesse gleichzeitig lesen/schreiben
			fdc_lock = LOCKED;
			floppy_read_sector (sektor, buf);
			floppy_cache[sektor] = (char*)malloc (512); // Sektor cachen
			memcpy ((void*)floppy_cache[sektor], buf, 512);
			fdc_lock = UNLOCKED;
			
			#ifdef LOAD_FLOPPY_IN_BACKGROUND
			fdd_read_req = FALSE;
			#endif
			//print ("FDD\n");
		
		} else { // Der Sektor liegt bereits im Cache => Von diesem Lesen
			memcpy (buf, (void*)floppy_cache[sektor], 512);
			//print ("CACHE\n");
		}
		
		memcpy (buffer, buf+offset, bytes); // Und rüber damit
		
		handle->fpos += bytes;
		
		return bytes;
		
	}
	
	return FS_ERR_EOF;
}

size_t fdc_write (FILE *handle, void *buffer, size_t bytes) {
	struct fdc_struct *h = fdc_get_handle (handle->handle);
	
	if (h == NULL) {
		return FS_ERR_EOF;
	}
	
	if (_strcmp (h->path, "/dev/fd")) {
		if (bytes > 512) { // Maximal ein Sektor pro fread
			bytes = 512;
		}
		
		char buf[512];
		size_t offset = handle->fpos % 512; // Verschiebung im Sektor
		uint32_t sektor = handle->fpos/512;
		
		if (bytes != 512) { // Wenn wieso der ganze Sektor überschrieben wird, können wir hier etwas Zeit sparen
			if (floppy_cache [sektor] != NULL) {
				memcpy (buf, (void*)floppy_cache[sektor], 512);
			} else {
				#ifdef LOAD_FLOPPY_IN_BACKGROUND
				while (fdd_read_req);
				fdd_read_req = TRUE;
				#endif
				lock_wait (&fdc_lock);
				fdc_lock = LOCKED;
				floppy_read_sector (sektor, buf); // Sektor auslesen
				fdc_lock = UNLOCKED;
				#ifdef LOAD_FLOPPY_IN_BACKGROUND
				fdd_read_req = FALSE;
				#endif
			}
		}
		
		memcpy (buf+offset, buffer, bytes); // Änderungen vornehmen
		
		// Und wieder in den Cache damit
		if (floppy_cache[sektor] == NULL) {
			floppy_cache[sektor] = (char*)malloc (52);
		}
		
		memcpy ((void*)floppy_cache[sektor], buf, 512);
		
		lock_wait (&fdc_write_lock);
		fdc_write_lock = LOCKED;
		#if WRITE_CACHE_SIZE > 0
		int q; // Wenn der Sektor schon in der Schlange ist, müssen wir ihn nicht noch hinzufügen
		for (q=0; q<queue_len; q++) {
			if (queue[q] == sektor) {
				q = WRITE_CACHE_SIZE;
				break;
			}
		}
		if (q < WRITE_CACHE_SIZE) {
			while (queue_len >= WRITE_CACHE_SIZE); // Warten bis wieder Platz ist
			queue[queue_len] = sektor;
			queue_len++;
		}
		#else // Kein Schreibcache
		floppy_write_sector (handle->fpos/512, buf); // Und wieder schreiben
		#endif
		fdc_write_lock = UNLOCKED;
		
		handle->fpos += bytes;
		
		return bytes;
		
	}
	
	return FS_ERR_EOF;
}

int fdc_close (FILE *handle) {
	fdc_del_handle (handle->handle);
	return FS_ERR_SUCCESS;
}

int fdc_seek (FILE *handle, int offset, int origin) {
	struct fdc_struct *h = fdc_get_handle (handle->handle);
	int pos = handle->fpos;
	
	switch (origin) {
		case SEEK_SET:
			pos = offset;
			break;
		case SEEK_CUR:
			pos += offset;
			break;
		case SEEK_END:
			pos = h->filelen + offset;
			break;
	}
	
	if (pos < 0) { // Bei negativen Werten ist die Position relativ zum Disketten Ende
		int sub = (pos*-1) % h->filelen;
		pos = h->filelen - sub;
	}
	
	if (pos > h->filelen) { // Bei Floppy ist das Käse
		return 1; // Fehler
	}
	
	handle->fpos = pos;
	
	return 0;
}

size_t fdc_tell (FILE *handle) {
	return handle->fpos;
}